package com.github.yoojia.flow.devices;

import com.github.yoojia.flow.Context;
import com.github.yoojia.flow.PiConfig;
import com.github.yoojia.flow.VirtualDevice;
import com.parkingwang.lang.data.Config;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalOutput;
import com.pi4j.io.gpio.PinState;
import com.pi4j.io.gpio.RaspiPin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Timer;
import java.util.TimerTask;

import static com.github.yoojia.flow.util.ConfigKit.checkRequired;

/**
 * @author 2017 Yoojia Chen (yoojiachen@gmail.com)
 */
public abstract class VirtualDigitOutputDevice extends VirtualDevice {

    private static final String TAG = VirtualDigitOutputDevice.class.getSimpleName();
    private static final Logger LOGGER = LoggerFactory.getLogger(VirtualDigitOutputDevice.class);

    private GpioPinDigitalOutput mDigitPin;

    private final Timer mTimer = new Timer("DigitOutputTimer", true);

    private PinState mDefaultState;
    private int mResetPinStateTimeout;
    private PinState mOffState;

    @Override
    public void onInit(Context context, Config configs) throws Exception {
        super.onInit(context, configs);
        checkRequired(configs, PiConfig.NAME_PIN_ADDRESS, TAG);
        checkRequired(configs, PiConfig.NAME_PIN_INIT_STATE, TAG);

        mResetPinStateTimeout = configs.getInt(PiConfig.NAME_PIN_AUTO_RESET_TIMEOUT);

        final int address = configs.getInt(PiConfig.NAME_PIN_ADDRESS);
        final String defaultState = configs.getString(PiConfig.NAME_PIN_INIT_STATE).toUpperCase();
        LOGGER.info("Init OutputDigitDevice, UUID: {}, Address: {}, DefaultState: {}",
                getAddress(), address, defaultState);

        mDefaultState = parseState(defaultState);
        mOffState = parseState(configs.getString(PiConfig.NAME_PIN_OFF_STATE, defaultState));

        mDigitPin = GpioFactory.getInstance()
                .provisionDigitalOutputPin(
                        RaspiPin.getPinByAddress(address),
                        getAddress(),
                        mDefaultState);

        if (mDigitPin == null) {
            throw new IllegalStateException("Fail to bind Output pin: " + address);
        }

        mDigitPin.setShutdownOptions(true, mDefaultState);
    }

    @Override
    public void onStart() {
        // nop
    }

    @Override
    public void onStop() {
        mTimer.purge();
        mTimer.cancel();
    }

    public PinState getOffState() {
        return mOffState;
    }

    public PinState getDefaultState() {
        return mDefaultState;
    }

    protected GpioPinDigitalOutput getDigitPin() {
        return mDigitPin;
    }

    public final void setHigh(){
        mDigitPin.high();
        tryAutoResetToOff();
    }

    public final void setLow(){
        mDigitPin.low();
        tryAutoResetToOff();
    }

    public final void toggle(){
        mDigitPin.toggle();
    }

    private void tryAutoResetToOff(){
        final PinState offState = getOffState();
        if (mResetPinStateTimeout > 0 &&
                ! getDigitPin().getState().equals(offState)){ // 在ON状态下，自动重置
            mTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    LOGGER.debug("Auto Set OFF, delay: {}ms", mResetPinStateTimeout);
                    mDigitPin.setState(offState);
                }
            }, mResetPinStateTimeout);
        }
    }

    private PinState parseState(String stateName){
        return PiConfig.STATE_LOW.equals(stateName) ? PinState.LOW : PinState.HIGH;
    }

}
